﻿namespace Legend.Reflection
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Reflection;

    /// <summary>
    /// Provides a common interface for PropertyInfo and FieldInfo.
    /// </summary>
    [CLSCompliant(true)]
    public abstract class ValueMemberInfo
        : MemberInfo
    {
        protected abstract MemberInfo WrappedMemberInfo
        {
            get;
        }

        /// <summary>
        /// <see cref="MemberInfo.DeclaringType"/>
        /// </summary>
        public override Type DeclaringType
        {
            get
            {
                return this.WrappedMemberInfo.DeclaringType;
            }
        }

        /// <summary>
        /// <see cref="MemberInfo.GetCustomAttributes"/>
        /// </summary>
        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            return this.WrappedMemberInfo.GetCustomAttributes(attributeType, inherit);
        }

        /// <summary>
        /// <see cref="MemberInfo.GetCustomAttributes"/>
        /// </summary>
        public override object[] GetCustomAttributes(bool inherit)
        {
            return this.WrappedMemberInfo.GetCustomAttributes(inherit);
        }

        /// <summary>
        /// <see cref="MemberInfo.IsDefined"/>
        /// </summary>
        public override bool IsDefined(Type attributeType, bool inherit)
        {
            return this.WrappedMemberInfo.IsDefined(attributeType, inherit);
        }

        /// <summary>
        /// <see cref="MemberInfo.MemberType"/>
        /// </summary>
        public override MemberTypes MemberType
        {
            get
            {
                return this.WrappedMemberInfo.MemberType;
            }
        }

        /// <summary>
        /// <see cref="MemberInfo.Name"/>
        /// </summary>
        public override string Name
        {
            get
            {
                return this.WrappedMemberInfo.Name;
            }
        }

        /// <summary>
        /// <see cref="MemberInfo.ReflectedType"/>
        /// </summary>
        public override Type ReflectedType
        {
            get
            {
                return this.WrappedMemberInfo.ReflectedType;
            }
        }

        /// <summary>
        /// Gets the <see cref="Type"/> of the member represented by this
        /// ValueMemberInfo.
        /// </summary>
        public abstract Type ValueType
        {
            get;
        }

        /// <summary>
        /// Sets a value to the member.
        /// </summary>
        /// <param name="o">The object where to set the value.</param>
        /// <param name="value">The value to set.</param>
        public abstract void SetValue(object o, object value);

        /// <summary>
        /// Gets a value from the member.
        /// </summary>
        /// <param name="o">The object where to get the value from.</param>
        /// <returns>The value of the member on the given object.</returns>
        public abstract object GetValue(object o);

        /// <summary>
        /// Creates a ValueMemberInfo.
        /// </summary>
        /// <param name="wrappedMemberInfo">The <see cref="PropertyInfo" /> or <see cref="FieldInfo" /> to wrap.</param>
        [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
        public static ValueMemberInfo Create(MemberInfo wrappedMemberInfo)
        {
            if (wrappedMemberInfo is PropertyInfo)
            {
                return new PropertyValueMemberInfo((PropertyInfo)wrappedMemberInfo);
            }
            
            if (wrappedMemberInfo is FieldInfo)
            {
                return new FieldValueMemberInfo((FieldInfo)wrappedMemberInfo);
            }
            
            throw new ArgumentException("Only PropertyInfos and FieldInfos can be wrapped in a ValueInfo.");
        }

        
        private class PropertyValueMemberInfo
            : ValueMemberInfo
        {
            private PropertyInfo wrappedMemberInfo;
            public PropertyValueMemberInfo(PropertyInfo wrappedMemberInfo)
            {
                this.wrappedMemberInfo = wrappedMemberInfo;
            }

            protected override MemberInfo WrappedMemberInfo
            {
                get
                {
                    return this.wrappedMemberInfo;
                }
            }

            public override Type ValueType
            {
                get
                {
                    return this.wrappedMemberInfo.PropertyType;
                }
            }

            public override void SetValue(object o, object value)
            {
                this.wrappedMemberInfo.SetValue(o, value, null);
            }

            public override object GetValue(object o)
            {
                return this.wrappedMemberInfo.GetValue(o, null);
            }
        }
        

        
        private class FieldValueMemberInfo
            : ValueMemberInfo
        {
            private FieldInfo wrappedMemberInfo;
            public FieldValueMemberInfo(FieldInfo wrappedMemberInfo)
            {
                this.wrappedMemberInfo = wrappedMemberInfo;
            }

            public override Type ValueType
            {
                get
                {
                    return this.wrappedMemberInfo.FieldType;
                }
            }

            public override void SetValue(object o, object value)
            {
                this.wrappedMemberInfo.SetValue(o, value);
            }

            public override object GetValue(object o)
            {
                return this.wrappedMemberInfo.GetValue(o);
            }

            protected override MemberInfo WrappedMemberInfo
            {
                get
                {
                    return this.wrappedMemberInfo;
                }
            }
        }
        
    }
}
